//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
#include "globals.h"

static s8 attach(struct client *c,s32 surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<5){
    PERR("client(%d):fatal:surface::attach missing data\n",c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):surface::attach buffer_id=%u x=%d y=%d\n",c->so,
                                       m->req[2],(s32)m->req[3],(s32)m->req[4]);

  r=surface_attach(c,surface_slot,m->req[2],(s32)m->req[3],(s32)m->req[4]);
exit:
  return r;
}

static s8 damage(struct client *c,s32 surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<6){
    PERR("client(%d):fatal:surface::damage missing data\n",c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):surface::damage x=%d y=%d w=%d h=%d\n",c->so,
                   (s32)m->req[2],(s32)m->req[3],(s32)m->req[4],(s32)m->req[5]);

  r=surface_damage(c,surface_slot,(s32)m->req[2],(s32)m->req[3],
                                                 (s32)m->req[4],(s32)m->req[5]);
exit:
  return r;
}

static s8 frame(struct client *c,s32 surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<3){
    PERR("client(%d):fatal:surface::frame missing callback_id\n",c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):surface::frame callback_id=%u\n",c->so,m->req[2]);

  r=surface_frame(c,surface_slot,m->req[2]);
exit:
  return r;
}

static s8 set_opaque_region(struct client *c,s32 surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<3){
    PERR("client(%d):fatal:surface::set_opaque_region missing region_id rom\n",
                                                                         c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):surface::set_opaque_region region_id=%u\n",c->so,
                                                                     m->req[2]);

  r=surface_set_opaque_region(c,surface_slot,m->req[2]);
exit:
  return r;
}

static s8 set_input_region(struct client *c,s32 surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<3){
    PERR("client(%d):fatal:surface::set_input_region missing region_id\n",
                                                                         c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):surface::set_input_region region_id=%u\n",c->so,
                                                                     m->req[2]);

  r=surface_set_input_region(c,surface_slot,m->req[2]);
exit:
  return r;
}

s8 wl_surface_req(struct client *c,s32 surface_slot,struct msg *m)
{
  s8 r;
  switch(m->req_op){
  case WL_SURFACE_DESTROY:
    LOG_WIRE("client(%d):surface::destroy surface_id=%u\n",c->so,m->req[0]);
    r=surface_destroy(c,surface_slot);
    break;
  case WL_SURFACE_ATTACH:
    r=attach(c,surface_slot,m);
    break;
  case WL_SURFACE_DAMAGE:
    r=damage(c,surface_slot,m);
    break;
  case WL_SURFACE_FRAME:
    r=frame(c,surface_slot,m);
    break;
  case WL_SURFACE_SET_OPAQUE_REGION:
    r=set_opaque_region(c,surface_slot,m);
    break;
  case WL_SURFACE_SET_INPUT_REGION:
    r=set_input_region(c,surface_slot,m);
    break;
  case WL_SURFACE_COMMIT:
    LOG_WIRE("client(%d):surface::commit surface_id=%u\n",c->so,m->req[0]);
    r=surface_commit(c,surface_slot);
    break;
  case WL_SURFACE_SET_BUFFER_TRANSFORM:
    LOG_WIRE("client(%d):surface::set_buffer_transform ignored\n",c->so);
    r=LWL_OK;
    //NOOP
    break;
  default:
    PERR("client(%d):fatal:surface unknown opcode %u\n",c->so,m->req_op);
    r=LWL_ERR;
  };
  return r;
}

#define EV_ENTER_DWS 3
s8 wl_surface_ev_enter(struct client *c,u32 surface_id,u32 output_id)
{
  u32 ev[EV_ENTER_DWS];

  ev[0]=surface_id;
  u16 ev_sz=EV_ENTER_DWS*sizeof(u32);
  ev[1]=ev_sz<<16|WL_SURFACE_ENTER;
  ev[2]=output_id;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):surface::enter surface_id=%u output_id\n",c->so,
                                                          surface_id,output_id);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):surface::enter unable to send event)\n",c->so,
                                                                             r);
    r=LWL_ERR;goto exit;
  }

  if(r!=ev_sz){
    PERR("client(%d):fatal:surface::enter only %ld/%ld were sent\n",c->so,r,
                                                                         ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}

#define EV_LEAVE_DWS 3
s8 wl_surface_ev_leave(struct client *c,u32 surface_id,u32 output_id)
{
  u32 ev[EV_LEAVE_DWS];

  ev[0]=surface_id;
  u16 ev_sz=EV_LEAVE_DWS*sizeof(u32);
  ev[1]=ev_sz<<16|WL_SURFACE_LEAVE;
  ev[2]=output_id;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):surface::leave surface_id=%u output_it=%u\n",c->so,
                                                          surface_id,output_id);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):surface::leave unable to send event\n",c->so,r);
    r=LWL_ERR;goto exit;
  }

  if(r!=ev_sz){
    PERR("client(%d):fatal:surface::leave only %ld/%ld were sent\n",c->so,r,
                                                                         ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}
